# encoding: utf-8
"""
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    created by lane.chang on '28/05/2024'
    comment: 用户意图相关逻辑
"""
import ujson
from typing import Optional

from config import Config
from project import logger
from project.lib.common import get_redis
from project.model.basic import User
from project.middleware.chatgpt import ChatGPT
from project.model.basic import Knowledge
from project.lib.decorator import retry
from project.model.flow import Flow
from project.service.agents.agent import Agent
from project.service.agents.nlg import NlgService
from project.service.agents.nlu import NluService
from project.service.agents.interaction import Interaction
from project.core.function import async_run, Core
from project.core.log import Colors


class AgentService:
    """
    """
    @staticmethod
    def get_user_memories_key(user: User):
        """
        :param user:
        :return:
        """
        return f'flow_memories_{user.dev_id}_{user.bot_id}_{user.user_id}'

    @staticmethod
    def is_new_flow(user: User, flow_code):
        """ 是否开启新流程
        :param user:
        :param flow_code:
        :return:
        """
        key = AgentService.get_user_memories_key(user)
        rds = get_redis()
        if rds.exists(key):
            return rds.hget(key, 'flow_code') != flow_code
        else:
            return True

    @staticmethod
    def add_user_memories(user: User, user_message, fix='用户对你说'):
        """ 添加用户流程缓存
        :param user:
        :param user_message:
        :param fix:
        :return:
        """
        key = AgentService.get_user_memories_key(user)
        rds = get_redis()
        user_memories = list()
        if rds.exists(key):
            user_memories = rds.hget(key, 'memories') or '[]'
            user_memories = ujson.loads(user_memories)

        user_memories.append(f'{fix}: {user_message}')
        rds.hset(key, 'memories', ujson.dumps(user_memories))

    @staticmethod
    def load_user_memories(user: User):
        """ 加载用户流程缓存
        :param user:
        :param fix:
        :return:
        """
        key = AgentService.get_user_memories_key(user)
        rds = get_redis()
        user_memories = rds.hget(key, 'memories') or '[]'
        user_memories = ujson.loads(user_memories)

        return user_memories

    @staticmethod
    def init_user_memories(user: User, flow_code, user_message):
        """ 初始化
        :param user:
        :param flow_code:
        :param user_message:
        :return:
        """
        key = AgentService.get_user_memories_key(user)
        rds = get_redis()
        rds.delete(key)

        rds.hset(key, 'flow_code', flow_code)
        rds.hset(key, 'memories', ujson.dumps([f'用户对你说: {user_message}']))

    @staticmethod
    @retry()
    async def detect_intention(user_message):
        """ 推测意图(苏康养)
        :return:
        """
        prompt = f"""
            ###Instruction
            请判断以下用户当前消息符合以下哪一个意图？请按输出格式要求输出，并输出你的推理过程。
    
            # 意图列表
            意图编号：video_link 意图内容：用户明确提到要视频通话或联系前台。
            意图编号：report 意图内容：用户要查看自己的睡眠报告。
            意图编号：other 意图内容：其他意图。
    
            # 输出格式
            用户对你说："...这里是用户对你说的话..." <reason>你的推理过程：...</reason><intent>意图编号</intent><message>你对用户说的话</message>
    
            # 输出样例
            用户对你说："我想联系前台" <reason> 用户明确提到要联系前台  </reason><intent>video_link</intent>
            用户对你说："我想看看我的睡眠报告" <reason>用户明确提到要查看自己的睡眠报告</reason><intent>report</intent>
            用户对你说："我昨天睡的怎么样？" <reason>可能用户在关心自己的睡眠数据，推出睡眠报告</reason><intent>report</intent>
            用户对你说："晚饭吃了吗？" <reason>用户的意图不在意图列表里，属于其他意图</reason><intent>other</intent> 
            
            ###Output
            用户对你说："{user_message}" 
        """
        chat_gpt = ChatGPT(platform='moonshot')
        res = await chat_gpt.json_loads_content(user_text=prompt, tags=('reason', 'intent', 'message'))
        # 无意图返回
        if not res.get('intent') or res.get('intent') not in ['video_link', 'report']:
            return
        res['response'] = res.pop('message', '')

        return res

    @staticmethod
    async def detect_service(user_message, services, samples=None):
        """ 推荐服务流程
        :param user_message:
        :param services:
        :param samples:
        :return:
        """
        if samples:
            samples = '\n            '.join(samples)

        prompt = f"""
            ###Instruction 
            假设你是一名服务人员，现在需要你根据用户对你说的话，判断用户需要哪项服务。根据用户对你说的话判断用户需要以下服务列表中的哪个服务，请返回服务名称，如果没有服务与用户的需求对应，则返回"没有服务对应"。
            
            # 特别说明:
            1): 如果用户仅仅只是说了一个名词，例如"洗衣机"，虽然跟"衣物清洗"服务有所关联，但是用户并没有明确表明自己的意愿，比如："我想xxx", "我要xxx"等。则这类都返回"没有服务对应"。
            2): 如果用户直接说服务名称，则返回服务名称。
            3): 用户说"给我下单吧"，这个意愿有点宽泛，并不能明确对应某个服务，返回"没有服务对应"。
            4): 用户说"xx点送过来"，这个意愿有点宽泛，并不能明确对应某个服务，返回"没有服务对应"。
            
            # 服务列表
            {services}
            
            # 输出样例
            {samples}
            用户对你说："给我一个火箭"。你判断用户需要的服务名称：<name>没有服务对应</name>
            
            ###Output
            用户对你说：\"{user_message}\"。你判断用户需要的服务名称：<name>服务名称</name>
        """
        print(prompt)
        chat_gpt = ChatGPT(platform='moonshot')
        res = await chat_gpt.json_loads_content(user_text=prompt, tags=('name',))
        res['intent'] = 'service'

        return res

    @staticmethod
    @retry()
    async def detect_fixed_qa(user_message, questions):
        """
        :param user_message:
        :param questions:
        :return:
        """
        prompt = f"""
###Instruction
康养机构预先设置了一些高频问答库，FAQ，当前你接收到了用户的一条消息，请严格根据知识库的信息回答用户，当用户说的话或提问与预先设置的FAQ库或知识库的内容都不相关时，你必须回复'None'
请按输出格式返回并输出你的推理过程。

# 康养机构预先设置的高频问答库
{questions}

#输出格式
用户对你说："...这里是用户对你说的话..." <reason>你的推理过程：...</reason> <answer>你回复用户的话</answer> 

#输出样例
用户对你说："wifi密码多少？" <reason>你的推理过程：用户询问的是WiFi密码，这与预先设置的FAQ库中的第2条信息匹配。</reason> <answer>WiFi密码是88888888。</answer>
用户对你说："你吃了吗？"<reason>你的推理过程：这与预先设置的FAQ库中的内容不匹配。所以我无法直接从FAQ库中找到答案。</reason> <answer>None</answer>
用户对你说："你是谁？"<reason>你的推理过程：用户对你的身份和能做的事情感到好奇，这与知识库中"自我介绍"匹配。</reason> <answer>你好，我是酒店管家Youdee，我能依赖床垫下的生命体征检测传感器晚上监测您的睡眠状况，为您生成睡眠报告。也能扫描您的面部为您做健康体检。同时您还可以通过我预定酒店餐食、让我送水、拖鞋等。</answer>
###Output
用户对你说："{user_message}"
        """
        chat_gpt = ChatGPT(platform='moonshot')
        res = await chat_gpt.json_loads_content(user_text=prompt, tags=('reason', 'answer'))
        answer = res.pop('answer', 'None')
        if answer == 'None':
            return
        res['intent'] = 'fixed_qa'
        res['response'] = answer

        return res

    @staticmethod
    async def get_global_intent(user: User,
                                user_message: str,
                                flows: list[Flow],
                                knowledge: Knowledge = None,
                                chat_model_name='',
                                agent_profile='',
                                user_role='',
                                memories_record=True,
                                session=None) -> Optional[dict]:
        """
        :param user: 用户信息
        :param user_message: 用户话术
        :param flows: 服务流程列表
        :param knowledge: 知识库
        :param chat_model_name: 模型名称
        :param memories_record:
        :param agent_profile:
        :param user_role:
        :param session:
        :return:
        """
        if memories_record:
            # 初始化及加载记忆
            agent = Agent(user, user_message=user_message, user_role=user_role)
            # 保存接收消息的记忆
            agent.add_user_memories(agent.format(user_message))
            logger.info(f'====>加载用户加载记忆: {agent.format(user_message)} user: {user.dict()}', font_color=Colors.PURPLE.value)

        # 并发执行nlu, nlg
        result = await async_run(
            Core(func=NluService.nlu(user, user_message, flows, chat_model_name=chat_model_name, agent_profile=agent_profile, user_role=user_role), name='nlu'),
            Core(func=NlgService.nlg(user, user_message, knowledge, chat_model_name=chat_model_name, agent_profile=agent_profile, user_role=user_role, flows=flows,
                                     session=session), name='nlg')
        )

        nlu = result.get('nlu')  # 全局意图: 睡眠报告、视频连接、推荐服务
        nlg = result.get('nlg')  # 知识库聊天

        # if nlu.get('intent') == 'service' and not nlu.get('response'):
        #     nlu['response'] = nlg.get('response')

        # 过滤掉不存在的服务
        if nlu and nlu.get('intent') == 'service' and nlu.get('action') not in [flow.title for flow in flows]:
            nlu = None

        res = nlu or nlg

        if res.get('response') and memories_record:
            # 初始化及加载记忆
            agent = Agent(user, user_message=user_message, user_role=user_role)
            # 保存接收消息的记忆
            agent.add_user_memories(agent.format(res['response'], _type='out'))
            logger.info(f'<====加载用户加载记忆: {agent.format(res["response"])} user: {user.dict()}', font_color=Colors.PURPLE.value)

        return res


    @staticmethod
    async def do_flow_policy(user: User, user_message,
                             flow: Flow,
                             knowledge: Knowledge = None,
                             is_init: bool = False,
                             memories_record = True,
                             chat_model_name='',
                             agent_profile='',
                             user_role='',
                             session=None):
        """ 节点智能交互
        :param user:
        :param user_message:
        :param flow:
        :param knowledge:
        :param is_init:
        :param memories_record: 记录记忆
        :param chat_model_name:
        :param agent_profile:
        :param user_role:
        :return:
        """
        anget = Agent(user, user_message, user_role=user_role)
        # 清空对话日志
        if is_init:
            anget.init_user_memories()

        # 添加对话日志
        if memories_record:
            anget.add_user_memories(anget.format(user_message))  # in

        ret_data = await Interaction.policy(user, user_message, flow, knowledge, chat_model_name=chat_model_name, agent_profile=agent_profile, user_role=user_role, session=session)
        ret_data['flow_code'] = flow.code

        if ret_data and memories_record:
            response = ret_data['response']
            anget.add_user_memories(anget.format(response, _type='out'))  # out

        return ret_data


    @staticmethod
    async def do_flow_interaction(user: User, user_message, flow: Flow, knowledge: Knowledge = None,
                                  is_init: bool = False,
                                  chat_model_name='',
                                  agent_profile='',
                                  user_role='',
                                  memories_record = True,
                                  session=None):
        """ 节点智能交互
        :param user:
        :param user_message:
        :param flow:
        :param knowledge:
        :param is_init:
        :param chat_model_name:
        :param memories_record:
        :param agent_profile:
        :param user_role
        :return:
        """
        anget = Agent(user, user_message)
        # 清空对话日志
        if is_init:
            anget.init_user_memories()

        # 添加对话日志
        if memories_record:
            anget.add_user_memories(anget.format(user_message))  # in

        ret_data = await Interaction.policy(user, user_message, flow, knowledge, chat_model_name=chat_model_name, agent_profile=agent_profile, user_role=user_role, session=session)
        ret_data['flow_code'] = flow.code

        if ret_data:
            response = ret_data['response']
            if memories_record:
                anget.add_user_memories(anget.format(response, _type='out'))  # out

            # 执行dst处理
            ret_data['selected_params'] = await Interaction.dst(user, response, flow, chat_model_name=chat_model_name, agent_profile=agent_profile, user_role=user_role)

        return ret_data


if __name__ == "__main__":

    # user = User()
    # user_message = '房间内水杯在哪里'
    # flows = [
    #     Flow(title='衣物清洗', description='为用户提供的衣物清洗服务'),
    #     Flow(title='订餐服务', description='为用户提供的送餐服务'),
    #     Flow(title='维修服务', description='为住户提供房屋内的设施设备维修服务')
    # ]
    #
    # result = asyncio.run(AgentService.get_global_intent(user, user_message, flows=flows, knowledge=None))
    # print(result)
    pass

